﻿using System;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Utilities;

namespace YJKGridTopologyCal.Index.Quadtree
{
	[Serializable]
	public class Node<T> : NodeBase<T>
	{
		public static Node<T> CreateNode(Envelope env)
		{
			Key key = new Key(env);
			return new Node<T>(key.Envelope, key.Level);
		}

		public static Node<T> CreateExpanded(Node<T> node, Envelope addEnv)
		{
			Envelope envelope = new Envelope(addEnv);
			if (node != null)
			{
				envelope.ExpandToInclude(node._env);
			}
			Node<T> node2 = Node<T>.CreateNode(envelope);
			if (node != null)
			{
				node2.InsertNode(node);
			}
			return node2;
		}

		public Node(Envelope env, int level)
		{
			this._env = env;
			this._level = level;
			this._centreX = (env.MinX + env.MaxX) / 2.0;
			this._centreY = (env.MinY + env.MaxY) / 2.0;
		}

		public Envelope Envelope
		{
			get
			{
				return this._env;
			}
		}

		protected override bool IsSearchMatch(Envelope searchEnv)
		{
			return this._env.Intersects(searchEnv);
		}

		public Node<T> GetNode(Envelope searchEnv)
		{
			int subnodeIndex = NodeBase<T>.GetSubnodeIndex(searchEnv, this._centreX, this._centreY);
			if (subnodeIndex != -1)
			{
				return this.GetSubnode(subnodeIndex).GetNode(searchEnv);
			}
			return this;
		}

		public NodeBase<T> Find(Envelope searchEnv)
		{
			int subnodeIndex = NodeBase<T>.GetSubnodeIndex(searchEnv, this._centreX, this._centreY);
			if (subnodeIndex == -1)
			{
				return this;
			}
			if (this.Subnode[subnodeIndex] != null)
			{
				return this.Subnode[subnodeIndex].Find(searchEnv);
			}
			return this;
		}

		public void InsertNode(Node<T> node)
		{
			Assert.IsTrue(this._env == null || this._env.Contains(node.Envelope));
			int subnodeIndex = NodeBase<T>.GetSubnodeIndex(node._env, this._centreX, this._centreY);
			if (node._level == this._level - 1)
			{
				this.Subnode[subnodeIndex] = node;
				return;
			}
			Node<T> node2 = this.CreateSubnode(subnodeIndex);
			node2.InsertNode(node);
			this.Subnode[subnodeIndex] = node2;
		}

		private Node<T> GetSubnode(int index)
		{
			if (this.Subnode[index] == null)
			{
				this.Subnode[index] = this.CreateSubnode(index);
			}
			return this.Subnode[index];
		}

		private Node<T> CreateSubnode(int index)
		{
			double x = 0.0;
			double x2 = 0.0;
			double y = 0.0;
			double y2 = 0.0;
			switch (index)
			{
			case 0:
				x = this._env.MinX;
				x2 = this._centreX;
				y = this._env.MinY;
				y2 = this._centreY;
				break;
			case 1:
				x = this._centreX;
				x2 = this._env.MaxX;
				y = this._env.MinY;
				y2 = this._centreY;
				break;
			case 2:
				x = this._env.MinX;
				x2 = this._centreX;
				y = this._centreY;
				y2 = this._env.MaxY;
				break;
			case 3:
				x = this._centreX;
				x2 = this._env.MaxX;
				y = this._centreY;
				y2 = this._env.MaxY;
				break;
			}
			return new Node<T>(new Envelope(x, x2, y, y2), this._level - 1);
		}

		private readonly Envelope _env;

		private readonly double _centreX;

		private readonly double _centreY;

		private readonly int _level;
	}
}
